feat(ai): resolve topic explanations server-side for follow-up chat#58
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
📝 WalkthroughWalkthroughAdds a ChangesTopic Explanation Resolution and Workspace Integration
Estimated code review effort: 3 (Moderate) | ~30 minutes Sequence Diagram(s)sequenceDiagram
participant User
participant WorkspaceChat
participant WorkspaceRoute as API route
participant Resolver as resolveTopicExplanation
User->>WorkspaceChat: Select topic context
WorkspaceChat->>WorkspaceChat: isTopicContextReady = true
WorkspaceChat->>WorkspaceRoute: POST action=EXPLAIN (branch, semester, topicId, subjectCode)
WorkspaceRoute->>Resolver: resolveTopicExplanation(context)
Resolver-->>WorkspaceRoute: topic, module, explanation, cached
WorkspaceRoute-->>WorkspaceChat: explanation response
WorkspaceChat->>WorkspaceChat: set topicExplanation, explanationCached, explanationLoading=false
WorkspaceChat-->>User: Render initial assistant explanation
User->>WorkspaceChat: Send follow-up question
WorkspaceChat->>WorkspaceRoute: POST question with topic context
WorkspaceRoute-->>WorkspaceChat: follow-up answer
WorkspaceChat-->>User: Render follow-up response
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
🎉 Congratulations @imuniqueshiv!Thank you for contributing to HyperLearningTech. Your pull request has been successfully merged into main. 📦 Merge Summary
🚀 Keep Contributing
Thank you for helping make HyperLearningTech better. Happy Coding! 🚀 |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
app/api/ai/workspace/route.ts (1)
66-76: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick winShort-circuit
resolveTopicExplanationwhen the body already carries topic context.
resolveTopicExplanationis awaited unconditionally before thebody.*precedence is applied. On a server-side cache miss this delegates togenerateTopicAnswer(action: "EXPLAIN"), so a fresh (and costly) LLM explanation is generated even when the client already suppliedtopic,module, andcachedExplanation. Only resolve when one of them is missing.♻️ Proposed guard
- const resolved = await resolveTopicExplanation({ - branch, - semester, - topicId, - subjectCode, - }); - - const topic = body.topic?.trim() || resolved.topic; - const moduleTitle = body.module?.trim() || resolved.module; - const cachedExplanation = - body.cachedExplanation?.trim() || resolved.explanation; + let topic = body.topic?.trim(); + let moduleTitle = body.module?.trim(); + let cachedExplanation = body.cachedExplanation?.trim(); + + if (!topic || !moduleTitle || !cachedExplanation) { + const resolved = await resolveTopicExplanation({ + branch, + semester, + topicId, + subjectCode, + }); + topic = topic || resolved.topic; + moduleTitle = moduleTitle || resolved.module; + cachedExplanation = cachedExplanation || resolved.explanation; + }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@app/api/ai/workspace/route.ts` around lines 66 - 76, The `resolveTopicExplanation` call in `route.ts` is being awaited even when `body.topic`, `body.module`, and `body.cachedExplanation` already provide all needed context, which can trigger an unnecessary `generateTopicAnswer` lookup and LLM explanation. Update the workspace route handler to short-circuit this path: only call `resolveTopicExplanation` when at least one of the three body fields is missing, and otherwise use the trimmed body values directly. Keep the fallback behavior intact for partial payloads by preserving the existing precedence logic around `resolved.topic`, `resolved.module`, and `resolved.explanation`.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@components/ai/workspace-chat.tsx`:
- Around line 111-190: Reset the explanation request state when the active topic
changes, and align the workspace payload with the route’s expected contract. In
`WorkspaceChat`, make sure `explanationRequestedRef` is cleared on `topicId`
changes so a new topic can fetch its own explanation, and ensure
`setExplanationLoading(false)` still runs after canceled requests. Also update
the non-topic request path in `workspace-chat.tsx` to send the full context
expected by the API, or adjust `workspace/route.ts` so plain `question` requests
are not treated as follow-ups unless `branch`, `semester`, and `topicId` are
present.
---
Nitpick comments:
In `@app/api/ai/workspace/route.ts`:
- Around line 66-76: The `resolveTopicExplanation` call in `route.ts` is being
awaited even when `body.topic`, `body.module`, and `body.cachedExplanation`
already provide all needed context, which can trigger an unnecessary
`generateTopicAnswer` lookup and LLM explanation. Update the workspace route
handler to short-circuit this path: only call `resolveTopicExplanation` when at
least one of the three body fields is missing, and otherwise use the trimmed
body values directly. Keep the fallback behavior intact for partial payloads by
preserving the existing precedence logic around `resolved.topic`,
`resolved.module`, and `resolved.explanation`.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 32886c84-b2aa-43eb-aeea-5eda1ca01054
📒 Files selected for processing (3)
app/api/ai/workspace/route.tscomponents/ai/workspace-chat.tsxlib/ai/resolve-topic-explanation.ts
| const explanationRequestedRef = useRef(false); | ||
|
|
||
| useEffect(() => { | ||
| if ( | ||
| !isTopicContextReady || | ||
| topicExplanation || | ||
| cachedExplanation || | ||
| explanationRequestedRef.current | ||
| ) { | ||
| return; | ||
| } | ||
|
|
||
| explanationRequestedRef.current = true; | ||
| let cancelled = false; | ||
|
|
||
| const loadExplanation = async () => { | ||
| setExplanationLoading(true); | ||
| setError(null); | ||
|
|
||
| try { | ||
| const response = await fetch(apiEndpoint, { | ||
| method: "POST", | ||
| headers: { "Content-Type": "application/json" }, | ||
| body: JSON.stringify({ | ||
| branch, | ||
| semester, | ||
| topicId, | ||
| subjectCode, | ||
| action: "EXPLAIN", | ||
| }), | ||
| }); | ||
|
|
||
| const data = (await response.json()) as WorkspaceResponse; | ||
|
|
||
| if (cancelled) return; | ||
|
|
||
| if (!response.ok || !data.answer) { | ||
| throw new Error(data.error || "Failed to load topic explanation."); | ||
| } | ||
|
|
||
| setTopicExplanation(data.answer); | ||
| setExplanationCached(data.cached); | ||
| setMessages([ | ||
| { | ||
| id: "initial-cached-explanation", | ||
| role: "assistant", | ||
| content: data.answer, | ||
| timestamp: new Date(), | ||
| }, | ||
| ]); | ||
| } catch (err: unknown) { | ||
| if (!cancelled) { | ||
| setError( | ||
| err instanceof Error | ||
| ? err.message | ||
| : "Failed to load topic explanation." | ||
| ); | ||
| } | ||
| } finally { | ||
| if (!cancelled) { | ||
| setExplanationLoading(false); | ||
| } | ||
| } | ||
| }; | ||
|
|
||
| void loadExplanation(); | ||
|
|
||
| return () => { | ||
| cancelled = true; | ||
| }; | ||
| }, [ | ||
| apiEndpoint, | ||
| branch, | ||
| cachedExplanation, | ||
| isTopicContextReady, | ||
| semester, | ||
| subjectCode, | ||
| topicExplanation, | ||
| topicId, | ||
| ]); |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
rg -nP -C4 '<WorkspaceChat\b' --type=tsx --type=ts
rg -nP -C3 'key=' $(fd -tf 'workspace-chat' -e tsx) 2>/dev/nullRepository: imuniqueshiv/HyperLearningTech
Length of output: 2018
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Locate the API route and the component usages relevant to topic switching/remounting.
rg -n --hidden --glob '!node_modules' --glob '!dist' --glob '!build' '/api/ai/workspace|workspace-chat|WorkspaceChat|topicId|branch|semester' components app pages src 2>/dev/null || true
# Inspect the workspace chat component around the effect and render paths.
file="components/ai/workspace-chat.tsx"
if [ -f "$file" ]; then
nl -ba "$file" | sed -n '1,260p'
fi
# Find the workspace API route file(s).
fd -tf 'workspace' app api pages src 2>/dev/null || trueRepository: imuniqueshiv/HyperLearningTech
Length of output: 15667
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Read the relevant source slices directly.
for file in \
"components/ai/workspace-chat.tsx" \
"components/ai/workspace-chat-loader.tsx" \
"app/api/ai/workspace/route.ts" \
"app/rgpv/[branch]/[semester]/[subject]/ai/page.tsx"
do
if [ -f "$file" ]; then
echo "===== $file ====="
sed -n '1,260p' "$file"
echo
fi
doneRepository: imuniqueshiv/HyperLearningTech
Length of output: 18876
Fix the workspace payload and reset the explanation request per topic.
components/ai/workspace-chat.tsx:explanationRequestedRefnever resets, so atopicIdchange can reuse stale explanation state; if the in-flight request is canceled,explanationLoadingcan also stay stuck.app/api/ai/workspace/route.ts/components/ai/workspace-chat.tsx: the non-topic path sendsquestionwithout topic context, but the route treats everyquestionas follow-up and now requiresbranch,semester, andtopicId, so general questions return 400.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@components/ai/workspace-chat.tsx` around lines 111 - 190, Reset the
explanation request state when the active topic changes, and align the workspace
payload with the route’s expected contract. In `WorkspaceChat`, make sure
`explanationRequestedRef` is cleared on `topicId` changes so a new topic can
fetch its own explanation, and ensure `setExplanationLoading(false)` still runs
after canceled requests. Also update the non-topic request path in
`workspace-chat.tsx` to send the full context expected by the API, or adjust
`workspace/route.ts` so plain `question` requests are not treated as follow-ups
unless `branch`, `semester`, and `topicId` are present.
Pull Request
Summary
Improve the AI workspace follow-up chat flow by resolving topic explanations server-side. This removes the dependency on client-provided cached explanations, improves reliability, and ensures follow-up questions work consistently in production.
Related Issue
N/A
Type of Change
What Changed?
Screenshots (UI Changes Only)
N/A (No visual UI changes)
Testing
npm run format:check.npm run lint.npm run typecheck.npm run build.Checklist
main.npm run format:checkpasses.npm run lintpasses.npm run typecheckpasses.npm run buildpasses.Additional Notes
This update improves the robustness of the AI workspace by ensuring follow-up conversations no longer depend on client-side cached explanations. Topic explanations are now resolved server-side with cache-aware fallback, providing a more reliable and production-ready experience.
Summary by CodeRabbit
New Features
Bug Fixes